package com.tang.crm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tang.crm.dto.UserDto;
import com.tang.crm.entity.User;
import com.tang.crm.entity.UserRole;
import com.tang.crm.mapper.UserMapper;
import com.tang.crm.mapper.UserRoleMapper;
import com.tang.crm.service.UserRoleService;
import com.tang.crm.utils.PhoneUtil;
import com.tang.crm.vo.UserVo;
import com.tang.crm.service.UserService;
import com.tang.crm.utils.AssertUtil;
import com.tang.crm.utils.Md5Util;
import com.tang.crm.utils.UserIDBase64;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;


@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private UserRoleService userRoleService;

    /**
     * 用户登录
     *    1. 参数判断，判断用户姓名、用户密码非空
     *       如果参数为空，抛出异常（异常被控制层捕获并处理）
     *    2. 调用数据访问层，通过用户名查询用户记录，返回用户对象
     *    3. 判断用户对象是否为空
     *       如果对象为空，抛出异常（异常被控制层捕获并处理）
     *    4. 判断密码是否正确，比较客户端传递的用户密码与数据库中查询的用户对象中的用户密码
     *       如果密码不相等，抛出异常（异常被控制层捕获并处理）
     *     5. 如果密码正确，登录成功
     *
     * @param user
     */
    @Override
    public UserVo login(User user) {
        checkLoginParams(user);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_name", user.getUserName());
        User loginUser = userMapper.selectOne(queryWrapper);
        AssertUtil.isTrue(loginUser == null, "用户不存在或已注销");
        String pwdMD5 = Md5Util.encode(user.getUserPwd());
        AssertUtil.isTrue(!pwdMD5.equals(loginUser.getUserPwd()), "密码错误");
        return buildUserInfo(loginUser);
    }

    /**
     * 1. 修改密码的参数校验
     *       判断原始密码(oldPassword)是否为空
     *       判断原始密码是否正确（查询的用户对象中的用户密码是否原始密码一致）
     *       断新密码(newPassword)是否为空
     *       判断新密码是否与原始密码一致 （不允许新密码与原始密码）
     *       判断确认密码(confirmPassword)是否为空
     *       判断确认密码是否与新密码一致
     * 2. 设置用户新密码：进行MD5加密
     * 3. 执行更新操作
     * @param id
     * @param oldPassword
     * @param newPassword
     * @param confirmPassword
     */
    @Override
    public void updateUserPassword(Integer id, String oldPassword, String newPassword, String confirmPassword) {
        User user = userMapper.selectById(id);
        checkParams(user, oldPassword, newPassword, confirmPassword);
        user.setUserPwd(Md5Util.encode(newPassword));
        AssertUtil.isTrue(userMapper.updateById(user), "修改密码失败");
    }

    /**
     * 查询所有的销售人员
     * @return
     */
    @Override
    public List<Map<String, Object>> queryAllSales() {
        return userMapper.queryALLSales();
    }

    /**
     * 多条件用户查询
     * @param userDto
     * @return
     */
    @Override
    public Map<String, Object> selectByParams(UserDto userDto) {
        Map<String, Object> map = new HashMap<>();
        IPage<User> page = new Page<>(userDto.getPage(), userDto.getLimit());
        IPage<Map<String, Object>> mapIPage = userMapper.selectByParams(page, userDto);
        map.put("code", 0);
        map.put("msg", "");
        map.put("count", mapIPage.getTotal());
        map.put("data", mapIPage.getRecords());
        return map;
    }

    /**
     * 添加用户
     *  1. 参数校验
     *      用户名userName     非空，唯一性
     *      邮箱email          非空
     *      手机号phone        非空，格式正确
     *  2. 设置参数的默认值
     *      isValid           1
     *      createDate        系统当前时间
     *      updateDate        系统当前时间
     *      默认密码            123456 -> md5加密
     *  3. 执行添加操作，判断受影响的行数
     * @param user
     */
    @Override
    @Transactional
    public void saveUser(User user) {
        /* 参数校验 */
        checkUserParams(user);

        /* 设置参数的默认值 */
        user.setIsValid(1);
        user.setCreateDate(new Date());
        user.setUpdateDate(new Date());
        // 默认密码 123456 -> md5加密
        user.setUserPwd(Md5Util.encode("123456"));

        /* 执行添加操作，判断受影响的行数 */
        AssertUtil.isTrue(userMapper.insert(user) != 1, "添加用户失败");

        /* 用户角色关联 */
        relationUserRole(user.getId(), user.getRoleIds());
    }

    /**
     * 更新用户
     *  1. 参数校验
     *      判断用户ID是否为空，且数据存在
     *      用户名userName     非空，唯一性
     *      邮箱email          非空
     *      手机号phone        非空，格式正确
     *  2. 设置参数的默认值
     *      updateDate        系统当前时间
     *  3. 执行更新操作，判断受影响的行数
     * @param user
     */
    @Override
    public void updateUser(User user) {
        /* 参数校验 */
        // 判断用户ID是否为空
        AssertUtil.isTrue(null == user.getId(), "用户数据不存在");
        // 判断数据是否存在
        AssertUtil.isTrue(null == userMapper.selectById(user.getId()), "用户数据不存在");
        checkUserParams(user);

        /* 设置参数的默认值 */
        // 更新updateTime
        user.setUpdateDate(new Date());

        /* 执行更新操作，判断受影响的行数 */
        AssertUtil.isTrue(userMapper.updateById(user) != 1, "更新数据失败");

        /* 用户角色关联 */
        relationUserRole(user.getId(), user.getRoleIds());
    }

    /**
     * 删除用户
     * @param idList
     */
    @Override
    public void deleteUser(List<Integer> idList) {
        // 判断ids是否为空，长度是否大于0
        AssertUtil.isTrue(idList == null || idList.size() == 0, "待删除记录不存在");
        // 执行删除操作，判断受影响的行数
        AssertUtil.isTrue(userMapper.deleteBatchIds(idList) != idList.size(), "删除失败");
    }

    /**
     * 查询所有的客户经理
     * @return
     */
    @Override
    public List<Map<String, Object>> selectAllManagers() {
        return userMapper.selectAllManagers();
    }

    /**
     *  参数校验
     *    用户名userName     非空，唯一性
     *    邮箱email          非空
     *    手机号phone        非空，格式正确
     * @param user
     */
    public void checkUserParams(User user){
        // 判断用户名是否为空
        AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()), "请输入用户名");
        // 判断用户名的唯一性
        // 通过用户名查询用户对象
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserName, user.getUserName());
        User temp = userMapper.selectOne(queryWrapper);
        // 如果用户对象为空，则表示用户名可用；如果用户对象不为空，则表示用户名不可用
        // 如果是添加操作，数据库中无数据，只要通过名称查到数据，则表示用户名被占用
        // 如果是修改操作，数据库中有对应的记录，通过用户名查到数据，可能是当前记录本身，也可能是别的记录
        // 如果用户名存在，且与当前修改记录不是同一个，则表示其他记录占用了该用户名，不可用
        AssertUtil.isTrue(null != temp && !(temp.getId().equals(user.getId())), "用户名已存在，请重新输入！");
        // 邮箱 非空
        AssertUtil.isTrue(StringUtils.isBlank(user.getEmail()), "请输入邮箱");
        // 手机号 非空
        AssertUtil.isTrue(StringUtils.isBlank(user.getPhone()), "请输入手机号");
        // 判断手机号码的格式
        AssertUtil.isTrue(!PhoneUtil.isMobile(user.getPhone()), "手机号格式不正确！");
    }

    /**
     * 判断登录用户的用户名和密码是否为空
     * @param user
     */
    public void checkLoginParams(User user){
        AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()), "用户名不能为空");
        AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()), "密码不能为空");
    }

    /**
     * 将User对象封装成UserVo对象
     * @param user
     * @return
     */
    public UserVo buildUserInfo(User user){
        UserVo userVo = new UserVo();
        // 加密userID
        userVo.setUserIdStr(UserIDBase64.encoderUserID(user.getId()));
        userVo.setUserName(user.getUserName());
        userVo.setTrueName(user.getTrueName());
        return userVo;
    }

    /**
     * 检查修改密码的各个参数
     * @param user
     * @param oldPassword
     * @param newPassword
     * @param confirmPassword
     */
    public void checkParams(User user, String oldPassword, String newPassword, String confirmPassword){
        // 判断原始密码是否为空
        AssertUtil.isTrue(StringUtils.isBlank(oldPassword), "请输入原始密码");
        // 判断原始密码是否与数据库中的一致
        AssertUtil.isTrue(!Md5Util.encode(oldPassword).equals(user.getUserPwd()), "原始密码不正确");
        // 判断新密码是否为空
        AssertUtil.isTrue(StringUtils.isBlank(newPassword), "请输入新密码");
        // 判断新密码是否与原始密码一致
        AssertUtil.isTrue(oldPassword.equals(newPassword), "不能与原始密码相同");
        // 判断确认密码是否为空
        AssertUtil.isTrue(StringUtils.isBlank(confirmPassword), "请再次输入新密码");
        // 判断两次新密码是否一致
        AssertUtil.isTrue(!newPassword.equals(confirmPassword), "新密码与确认密码不一致");
    }

    /**
     * 用户角色关联
     *  添加操作
     *      原始角色不存在
     *          1. 不添加新的角色记录    不操作用户角色表
     *          2. 添加新的角色记录      给指定用户绑定相关的角色记录
     *
     *  更新操作
     *      原始角色不存在
     *          1. 不添加新的角色记录     不操作用户角色表
     *          2. 添加新的角色记录       给指定用户绑定相关的角色记录
     *      原始角色存在
     *          1. 添加新的角色记录       判断已有的角色记录不添加，添加没有的角色记录
     *          2. 清空所有的角色记录     删除用户绑定角色记录
     *          3. 移除部分角色记录       删除不存在的角色记录，存在的角色记录保留
     *          4. 移除部分角色，添加新的角色    删除不存在的角色记录，存在的角色记录保留，添加新的角色
     *
     *   如何进行角色分配？？？
     *      判断用户对应的角色记录存在，先将用户原有的角色记录删除，再添加新的角色记录
     *
     *  删除操作
     *      删除指定用户绑定的角色记录
     * @param userId
     * @param roleIds
     */
    public void relationUserRole(Integer userId, String roleIds){
        // 通过用户ID查询角色记录
        int count = userRoleMapper.countUserRoleByUserId(userId);
        // 判断角色记录是否存在
        if (count > 0){
            // 如果角色记录存在，则删除该用户对应的角色记录,之后在添加(对于更新操作)
            AssertUtil.isTrue(userRoleMapper.deleteUserRoleByUserId(userId) != count, "用户角色分配失败");
        }

        // 判断角色ID是否存在，如果存在，则添加该用户对应的角色记录
        if (!StringUtils.isBlank(roleIds)){
            // 将用户角色数据设置到集合中，执行批量添加
            List<UserRole> userRoleList = new ArrayList<>();
            // 将角色ID字符串转换成数组
            String[] userRoleIds = roleIds.split(",");
            // 遍历数组，得到对应的用户角色对象，并设置到集合中
            for (String roleId : userRoleIds) {
                UserRole userRole = new UserRole();
                userRole.setRoleId(Integer.parseInt(roleId));
                userRole.setUserId(userId);
                userRole.setCreateDate(new Date());
                userRole.setUpdateDate(new Date());
                // 设置到集合中
                userRoleList.add(userRole);
            }
            AssertUtil.isTrue(!userRoleService.saveBatch(userRoleList), "用户角色分配失败");
        }
    }

}
